﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO.Packaging;
using System.Data;
using Quexcel.OpenXML.Exceptions;
using System.IO;
using Quexcel.OpenXML.Interfaces;
using Quexcel.OpenXML.Excel.Factory;
using Quexcel.OpenXML.Excel.Worksheet;

namespace Quexcel.OpenXML.Transaction
{
    public class ExcelTransaction : IDisposable
    {
        public TransactionStatus Status { get; set; }

        internal ExcelConnection connection;
        internal long TransactionID { get; set; }

        private Dictionary<string, Worksheet> openDocs;
        private bool IsDisposed = false;

        public ExcelTransaction(ExcelConnection connection)
        {
            if (connection == null || connection.State == ConnectionState.Broken)
                throw new InvalidConnectionStateException(connection.State, "Invalid connection passed to transaction.");
            else
                TransactionID = connection.AddTransaction(this);
            this.connection = connection;
            this.openDocs = new Dictionary<string, Worksheet>();
            this.Status = TransactionStatus.Open;
        }

        public bool Commit()
        {
            Dictionary<PackagePart, XmlDocument> tempOpenWorksheets = new Dictionary<PackagePart, XmlDocument>();
            try
            {
                bool status = true;
                if (Status == TransactionStatus.Open)
                {
                    Status = TransactionStatus.Running;
                    foreach (string name in openDocs.Keys)
                    {
                        if (openDocs[name].HasWriter())
                            status = status && openDocs[name].GetWriter().Flush();
                        else if (openDocs[name].HasReader())
                            openDocs[name].GetReader().Close();
                    }

                    if (connection.workbook.HasWriter())
                        connection.workbook.GetWriter().Flush();
                    else if (connection.workbook.HasReader())
                        connection.workbook.GetReader().Close();
                }
                else
                    status = false;
                return status;
            }
            catch
            {
                return false;
            }
            finally
            {
                Status = TransactionStatus.Open;
                openDocs.Clear();
            }
        }

        public bool Rollback()
        {
            try
            {
                bool status = true;
                if (Status == TransactionStatus.Open)
                {
                    Status = TransactionStatus.Running;
                    foreach (string name in openDocs.Keys)
                    {
                        if (openDocs[name].HasWriter())
                        {
                            status = status && openDocs[name].GetWriter().Reset();
                        }
                    }

                    if (connection.workbook.HasWriter())
                        status = status && connection.workbook.GetWriter().Reset();
                }
                else
                    status = false;
                return status;
            }
            catch
            {
                return false;
            }
            finally
            {
                Status = TransactionStatus.Open;
                openDocs.Clear();
            }
        }

        internal Worksheet GetWorksheet(string sheetName)
        {
            if (openDocs.ContainsKey(sheetName.ToLower()))
                return openDocs[sheetName.ToLower()];
            else
                return null;
        }

        internal bool AddWorksheet(Worksheet worksheet)
        {
            if (openDocs.ContainsKey(worksheet.WorksheetName.ToLower()))
                return true;
            else
            {
                openDocs[worksheet.WorksheetName.ToLower()] = worksheet;
                return true;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            try
            {
                if (!IsDisposed)
                {
                    this.Commit();
                }
            }
            finally
            {
                if (!IsDisposed)
                {
                    this.Status = TransactionStatus.Closed;
                    openDocs = null;
                    IsDisposed = true;
                }
            }
        }

        #endregion
    }

    public enum TransactionStatus
    {
        Open,
        Closed,
        Running
    }
}